/****************************************************************
 *  This file created by writerB.php on 03/20/2007 at 22:47:44  *
 ****************************************************************
 *  writerB.php created by Tonk (tonk@ctonk.com) in 2007        *
 ****************************************************************
 *                 General Document Information
 *
 *  File Name: character.cpp
 *  Class Name: Character
 *  Parent Classes: Creature 
 *  Data members: 39
 *  Notes:
 ****************************************************************/

#include <cstdarg>
#include <cstring>
#include <iostream>
#include <vector>
#include "definitions.h"
#include "combined.h"
#include "log.h"
#include "note.h"
#include "socket.h"
#include "stringutil.h"
#include "timestamp.h"
#include "world.h"



int Character::numCharacters = 0;
int Character::characterIndex = 0;


// Default constructor...
Character::Character( bool copyover ): Creature::Creature() {
  setId( 0 );
  setGotInput( false );
  setDisconnected( false );
  setFlags( 0 );
  setSocket( ( copyover ) ? new Socket : NULL );
  setRoom( NULL );
  setBoard( NULL );
  setTedit( false );
  setStatus( 0 );
  setBankGold( 0 );
  setBankSilver( 0 );
  setMobDeaths( 0 );
  setMobKills( 0 );
  setPkDeaths( 0 );
  setPkKills( 0 );
  setExp( 0 );
  setTnl( 0 );
  setPractices( 0 );
  setTrains( 0 );
  setGains( 0 );
  setHand( 0 );
  setPoints( 0 );
  setEcho( '\0' );
  // Increment the object count...
  numCharacters++;
}

// Additional constructors...

// Copy constructor (Doesn't change _CharacterID)...
Character::Character( const Character& ref ): Creature::Creature( ref ) {
  setId( ref.getId() );
  setGotInput( ref.getGotInput() );
  setDisconnected( ref.getDisconnected() );
  setReply( ref.getReply() );
  setLastInput( ref.getLastInput() );
  setRepeat( ref.getRepeat() );
  setPrompt( ref.getPrompt() );
  setPoofin( ref.getPoofin() );
  setPoofout( ref.getPoofout() );
  setPassword( ref.getPassword() );
  setLastName( ref.getLastName() );
  setProfession( ref.getProfession() );
  setClan( ref.getClan() );
  setTitle( ref.getTitle() );
  setEyeDesc( ref.getEyeDesc() );
  setEyeColor( ref.getEyeColor() );
  setHairDesc( ref.getHairDesc() );
  setHairColor( ref.getHairColor() );
  setHouse( ref.getHouse() );
  setRoom( NULL );
  setSocket( ref.getSocket() );
  setObject( ref.getObject() );
  setBoard( ref.getBoard() );
  setNote( ref.getNote() );
  setFlags( ref.getFlags() );
  setStatus( ref.getStatus() );
  setBankGold( ref.getBankGold() );
  setBankSilver( ref.getBankSilver() );
  setMobDeaths( ref.getMobDeaths() );
  setMobKills( ref.getMobKills() );
  setPkDeaths( ref.getPkDeaths() );
  setPkKills( ref.getPkKills() );
  setExp( ref.getExp() );
  setTnl( ref.getTnl() );
  setPractices( ref.getPractices() );
  setTrains( ref.getTrains() );
  setGains( ref.getGains() );
  setHand( ref.getHand() );
  setPoints( ref.getPoints() );
  setEcho( ref.getEcho() );
  // Increment the object count...
  numCharacters++;
}

// Destructor...
Character::~Character() {
  if ( getSocket() ) {
    if ( !getSocket()->GetSocketIP().empty() ) {
      SocketServer::Instance()._socketList.remove( getSocket() );
      SocketServer::Instance().KillSocket( getSocket() );
      if ( getStatus() < CONNECTED )
        log( getSocket()->GetSocketIP() + " has disconnected.", SERVER );
      else
        log( getName() + " has disconnected from " + getSocket()->GetSocketIP(), SERVER );
    }
    delete getSocket();
    setSocket( NULL );
  }
  // Decrement the object count...
  numCharacters--;
}

// Assignment operator (Doesn't change _CharacterID)...
Character& Character::operator = ( const Character& ref ) {
  setId( ref.getId() );
  setGotInput( ref.getGotInput() );
  setDisconnected( ref.getDisconnected() );
  setReply( ref.getReply() );
  setLastInput( ref.getLastInput() );
  setRepeat( ref.getRepeat() );
  setPrompt( ref.getPrompt() );
  setPoofin( ref.getPoofin() );
  setPoofout( ref.getPoofout() );
  setPassword( ref.getPassword() );
  setLastName( ref.getLastName() );
  setProfession( ref.getProfession() );
  setClan( ref.getClan() );
  setTitle( ref.getTitle() );
  setEyeDesc( ref.getEyeDesc() );
  setEyeColor( ref.getEyeColor() );
  setHairDesc( ref.getHairDesc() );
  setHairColor( ref.getHairColor() );
  setHouse( ref.getHouse() );
  setRoom( ref.getRoom() );
  setSocket( ref.getSocket() );
  setObject( ref.getObject() );
  setBoard( ref.getBoard() );
  setNote( ref.getNote() );
  setFlags( ref.getFlags() );
  setStatus( ref.getStatus() );
  setBankGold( ref.getBankGold() );
  setBankSilver( ref.getBankSilver() );
  setMobDeaths( ref.getMobDeaths() );
  setMobKills( ref.getMobKills() );
  setPkDeaths( ref.getPkDeaths() );
  setPkKills( ref.getPkKills() );
  setExp( ref.getExp() );
  setTnl( ref.getTnl() );
  setPractices( ref.getPractices() );
  setTrains( ref.getTrains() );
  setGains( ref.getGains() );
  setHand( ref.getHand() );
  setPoints( ref.getPoints() );
  setEcho( ref.getEcho() );
  return *this;
}

// Protected Methods...
void Character::setId( const unsigned& id ) { // protected
  _id = id;
}

// Public accessor methods...
unsigned Character::getId( void ) const { // public
  return _id;
}

void Character::setGotInput( const bool& gotInput ) {
  _gotInput = gotInput;
  return;
}

bool Character::getGotInput( void ) const {
  return _gotInput;
}

void Character::setDisconnected( const bool& disconnected ) {
  _disconnected = disconnected;
  return;
}

bool Character::getDisconnected( void ) const {
  return _disconnected;
}

void Character::setReply( const std::string& reply ) {
  _reply = reply;
  return;
}

std::string Character::getReply( void ) const {
  return _reply;
}

void Character::setLastInput( const std::string& lastInput ) {
  _lastInput = lastInput;
  return;
}

std::string Character::getLastInput( void ) const {
  return _lastInput;
}

void Character::setRepeat( const std::string& repeat ) {
  _repeat = repeat;
  return;
}

std::string Character::getRepeat( void ) const {
  return _repeat;
}

void Character::setPrompt( const std::string& prompt ) {
  _prompt = prompt;
  return;
}

std::string Character::getPrompt( void ) const {
  return _prompt;
}

void Character::setPoofin( const std::string& poofin ) {
  _poofin = poofin;
  return;
}

std::string Character::getPoofin( void ) const {
  return _poofin;
}

void Character::setPoofout( const std::string& poofout ) {
  _poofout = poofout;
  return;
}

std::string Character::getPoofout( void ) const {
  return _poofout;
}

void Character::setPassword( const std::string& password ) {
  _password = password;
  return;
}

std::string Character::getPassword( void ) const {
  return _password;
}

void Character::setLastName( const std::string& lastName ) {
  _lastName = lastName;
  return;
}

std::string Character::getLastName( void ) const {
  return _lastName;
}

void Character::setProfession( const std::string& profession ) {
  _profession = profession;
  return;
}

std::string Character::getProfession( void ) const {
  return _profession;
}

void Character::setClan( const std::string& clan ) {
  _clan = clan;
  return;
}

std::string Character::getClan( void ) const {
  return _clan;
}

void Character::setTitle( const std::string& title ) {
  _title = title;
  return;
}

std::string Character::getTitle( void ) const {
  return _title;
}

void Character::setEyeDesc( const std::string& eyeDesc ) {
  _eyeDesc = eyeDesc;
  return;
}

std::string Character::getEyeDesc( void ) const {
  return _eyeDesc;
}

void Character::setEyeColor( const std::string& eyeColor ) {
  _eyeColor = eyeColor;
  return;
}

std::string Character::getEyeColor( void ) const {
  return _eyeColor;
}

void Character::setHairDesc( const std::string& hairDesc ) {
  _hairDesc = hairDesc;
  return;
}

std::string Character::getHairDesc( void ) const {
  return _hairDesc;
}

void Character::setHairColor( const std::string& hairColor ) {
  _hairColor = hairColor;
  return;
}

std::string Character::getHairColor( void ) const {
  return _hairColor;
}

void Character::setHouse( const std::string& house ) {
  _house = house;
  return;
}

std::string Character::getHouse( void ) const {
  return _house;
}

void Character::setRoom( Room* room ) {
  if ( room ) {
    // It's easier to check for REDIT status and area permissions here, then
    // to check them everywhere in the REDIT code itself...
    if ( getStatus() == REDIT && !room->getArea()->hasPermission( this ) )
      setStatus( CONNECTED );
  }
  _room = room;
  return;
}

void Character::setSocket( Socket* socket ) {
  _socket = socket;
  return;
}

Socket* Character::getSocket( void ) const {
  return _socket;
}

void Character::setObject( Object* object ) {
  _object = object;
  return;
}

Object* Character::getObject( void ) const {
  return _object;
}

void Character::setBoard( Board* board ) {
  _board = board;
  return;
}

Board* Character::getBoard( void ) const {
  return _board;
}

void Character::setNote( Note* note ) {
  _note = note;
  return;
}

Note* Character::getNote( void ) const { // public
  return _note;
}

std::vector< Note* >& Character::getNotes( void ) { // public
  return _notes;
}

void Character::setFlags( const unsigned& flags ) {
  _flags = flags;
  return;
}

unsigned Character::getFlags( void ) const {
  return _flags;
}

void Character::setTedit( const bool& tedit ) {
  setFlags( ( tedit ) ? (getFlags() | CH_FLAG_TEDIT) : ((~CH_FLAG_TEDIT) & getFlags()) );
  return;
}

bool Character::getTedit( void ) const {
  return ( (getFlags() & CH_FLAG_TEDIT) == CH_FLAG_TEDIT ) ? true: false;
}

void Character::setOmninet( const bool& omninet ) { // public
  setFlags( ( omninet ) ? (getFlags() | CH_FLAG_OMNINET) : ((~CH_FLAG_OMNINET) & getFlags()) );
  return;
}

void Character::setOmninet( const unsigned& which, const bool& set ) { // public
  setFlags( ( set ) ? (getFlags() | which) : ((~which) & getFlags()) );
  return;
}

bool Character::getOmninet( void ) const { // public
  return ( (getFlags() & CH_FLAG_OMNINET) == CH_FLAG_OMNINET ) ? true : false;
}

bool Character::getOmninet( const unsigned& which ) const { // public
  return ( (getFlags() & which) == which ) ? true : false;
}

void Character::setStatus( const unsigned& status ) {
  _status = status;
  return;
}

unsigned Character::getStatus( void ) const {
  return _status;
}

void Character::setBankGold( const unsigned& bankGold ) {
  _bankGold = bankGold;
  return;
}

unsigned Character::getBankGold( void ) const {
  return _bankGold;
}

void Character::setBankSilver( const unsigned& bankSilver ) {
  _bankSilver = bankSilver;
  return;
}

unsigned Character::getBankSilver( void ) const {
  return _bankSilver;
}

void Character::setMobDeaths( const unsigned& mobDeaths ) {
  _mobDeaths = mobDeaths;
  return;
}

unsigned Character::getMobDeaths( void ) const {
  return _mobDeaths;
}

void Character::setMobKills( const unsigned& mobKills ) {
  _mobKills = mobKills;
  return;
}

unsigned Character::getMobKills( void ) const {
  return _mobKills;
}

void Character::setPkDeaths( const unsigned& pkDeaths ) {
  _pkDeaths = pkDeaths;
  return;
}

unsigned Character::getPkDeaths( void ) const {
  return _pkDeaths;
}

void Character::setPkKills( const unsigned& pkKills ) {
  _pkKills = pkKills;
  return;
}

unsigned Character::getPkKills( void ) const {
  return _pkKills;
}

void Character::setExp( const unsigned& exp ) {
  _exp = exp;
  return;
}

unsigned Character::getExp( void ) const {
  return _exp;
}

void Character::setTnl( const unsigned& tnl ) {
  _tnl = tnl;
  return;
}

unsigned Character::getTnl( void ) const {
  return _tnl;
}

void Character::setPractices( const unsigned& practices ) {
  _practices = practices;
  return;
}

unsigned Character::getPractices( void ) const {
  return _practices;
}

void Character::setTrains( const unsigned& trains ) {
  _trains = trains;
  return;
}

unsigned Character::getTrains( void ) const {
  return _trains;
}

void Character::setGains( const unsigned& gains ) {
  _gains = gains;
  return;
}

unsigned Character::getGains( void ) const {
  return _gains;
}

void Character::setHand( const unsigned& hand ) {
  _hand = hand;
  return;
}

unsigned Character::getHand( void ) const {
  return _hand;
}

void Character::setPoints( const unsigned& points ) {
  _points = points;
  return;
}

unsigned Character::getPoints( void ) const {
  return _points;
}

void Character::setEcho( const char& echo ) { // public
  _echo = echo;
  return;
}

char Character::getEcho( void ) const { // public
  return _echo;
}

// General methods...
void Character::Send( const std::string& message, ... ) {
  va_list args;
  char buf[MAX_BUFFER];
  std::string buf2;

  va_start( args, message );

  vsprintf( buf, message.c_str(), args );
  buf2 = getSocket()->GetOutput();
  buf2.append( buf );
  
  getSocket()->SetOutput( buf2 );
  va_end( args );
}

void Character::flushOutput( ) {
  std::string output;
  std::string buf;
  std::string buf2;
  std::string buf3;
  std::string colorClear;
  // This will add a prompt to the end of output.
  // Of course, one needs some text before it'll add a prompt or we
  // can add a prompt if we get some input and it calls for it. :)
  
  output = getSocket()->GetOutput();
  if ( output.empty() != true || _gotInput ) {
    //if ( this->waiting == false )  { // Don't send a prompt if there's delayed text going
      if ( !getGotInput() ) {
          output.insert( 0, "\n\r" );
      } else {
        // Only if there's no input set gotInput to false
        // we do it like this so there isn't an extra newline
        // everytime you get some output
        if ( getSocket()->GetInput().empty() )
          setGotInput( false );
      }//*/
      //output.append( "\n\r" + _handlers.front()->Prompt( (this) ) );
      switch ( getStatus() ) {
        case LOGIN:  // Do nothing
          break;
        case AEDIT:
          if ( getArea() == NULL )
            buf << "{x\n\r[no area selected]";
          else
            buf << "{x\n\r[area " << getArea()->getKeyword() << ']';
          break;
        case MEDIT:
          buf << "{x\n\r[mob " << getMob()->getVnum() << ']';
          break;
        case PEDIT:
          buf << "{x\n\r[player " << getCharacter()->getName() << ']';
          break;
        case REDIT:
          buf << "{x\n\r[room " << getRoom()->getVnum() << ']';
          break;
        default:
          buf << formatPrompt();
          break;
      }
      if ( getTedit() )
        buf = "[symphony text editor]";
      if ( getStatus() != LOGIN )
        output << "\n\r" << buf << "{x ";
      getSocket()->SetOutput( output );
    //}
  }

  // color parsing...
  std::string temp = getSocket()->GetOutput();
  for ( int x=0; x < (int)temp.size(); x++ ) {
    if ( temp[x] == '{' ) {
      buf3 << cprint_private( 'x' );
      if ( temp[x+1] == '!' || temp[x+1] == '@' ) {
        buf3 << cprint_private( temp[x+1], temp[x+2] );
        x += 2;
      }
      else {
        buf3 << cprint_private( temp[x+1] );
        x++;
      }
    }
    else
      buf3 << temp[x];
  }
  getSocket()->SetOutput( buf3 );

  return;
}

std::string Character::promptSwitch( char c ) {
  std::string replaced;

  switch ( c ) {
    case 'h': replaced << getHp(); break;
    case 'H': replaced << getMaxHp(); break;
    case 'm': replaced << getMana(); break;
    case 'M': replaced << getMaxMana(); break;
    case 'v': replaced << getMove(); break;
    case 'V': replaced << getMaxMove(); break;
    case 'g': replaced << getGold(); break;
    case 'G': replaced << getBankGold(); break;
    case 's': replaced << getSilver(); break;
    case 'S': replaced << getBankSilver(); break;
    case 'r': replaced << getRoom()->getVnum(); break;
    default: replaced << '%'; break;
  }

  return replaced;
}

std::string Character::formatPrompt( void ) {
  std::string unformatted = getPrompt();
  std::string formatted = "{x\n\r";

  for ( unsigned x=0; x < unformatted.size(); x++ ) {
    if ( unformatted[x] == '%' ) {
      formatted << Character::promptSwitch( unformatted[x+1] );
      x++;
    }
    else
      formatted << unformatted[x];
  }

  return formatted;
}

Note* Character::addNote( void ) { // public
  Note* note = new Note;
  getNotes().push_back( note );
  return note;
}

bool Character::save( void ) { // public
  std::string file = PFILE_DIR;
  file << lowerString( getName() ) << ".pfile";
  std::ofstream write( file.c_str() );
  if ( write.fail() ) {
    log( "Couldn't open " + file + " for writing.", SERVER );
    record( "Couldn't open " + file + " for writing." );
    return false;
  }
  Creature::save( write );

  write << "// Character data...\n";
  write  << getId() << ' ' << getPassword() << '\n'
      << getPrompt() << '\n' << getLastName() << '\n'
      << getProfession() << '\n' << getClan() << '\n';

  write << getBankGold() << ' ' << getBankSilver() << '\n';
  write << getMobDeaths() << ' ' << getMobKills() << ' ' << getPkDeaths() << ' ' << getPkKills() << '\n';
  write << getExp() << ' ' << getTnl() << '\n';
  write << getPractices() << ' ' << getTrains() << ' ' << getGains() << '\n';
  write << getOmninet() << '\n';
  write << getTitle() << '\n';
  write << getEyeDesc() << ' ' << getEyeColor() << '\n';
  write << getHairDesc() << ' ' << getHairColor() << '\n';
  write << getStrHeight() << ' ' << getStrWeight() << ' ' << getHand() << '\n';
  write << getHouse() << '\n';
  write << getPoints() << ' ' << getEcho() << '\n';
  write << getPoofin() << '\n' << getPoofout() << '\n';

  write << "\n// Unsent notes...";
  for ( std::vector< Note* >::iterator it = getNotes().begin(); it != getNotes().end(); it++ )
    write << *it;

  write << "\n\n// File written " << Timestamp::Instance().getDateTime() << '\n';

  write.close();
  write.clear();

    return true;
}

bool Character::load( void ) { // public                            FIXME
  std::string  file;
  char    password[2048], lastName[2048], clan[2048], profession[2048], title[2048],
        eyeDesc[2048], eyeColor[2048], hairDesc[2048], hairColor[2048], prompt[2048],
        strHeight[2048], strWeight[2048], house[2048], poofin[2048], poofout[2048], temp[2048];
  unsigned  id=0, bankGold=0, bankSilver=0, mobDeaths=0, mobKills=0,
        pkDeaths=0, pkKills=0, exp=0, tnl=0, practices=0, trains=0, gains=0,
        hand=0, points=0;
  bool    omninet;
  char    echo;

  file << PFILE_DIR << lowerString( getName() ) << ".pfile";

  std::ifstream read( file.c_str() );
  if ( read.fail() ) {
    log( "Couldn't open " + file + " for reading.", SERVER );
    record( "Couldn't open " + file + " for reading." );
    return false;
  }

  // Read in Creature data...
  Creature::load( read );

  // Put this Character in the room that was read in from Creature::load()
  getRoom()->add( this );

  // Read the Character data in...
  read >> id >> password;
  read.get(); read.getline( prompt, 1000 );
  read.getline( lastName, 1000 );
  read >> profession;
  read >> clan;
  read >> bankSilver >> bankGold;
  read >> mobDeaths >> mobKills >> pkDeaths >> pkKills;
  read >> exp >> tnl;
  read >> practices >> trains >> gains;
  read >> omninet;
  read.get(); read.getline( title, 1000 );
  read >> eyeDesc >> eyeColor;
  read >> hairDesc >> hairColor;
  read >> strHeight >> strWeight >> hand;
  read.get(); read.getline( house, 1000 );
  read >> points >> echo;
  read.get(); read.getline( poofin, 1000 );
  read.getline( poofout, 1000 );

  // Set our newly found Character data...
  setId( id );
  setPassword( password );
  setPrompt( prompt );
  setLastName( lastName );
  setProfession( profession );
  setClan( clan );
  setBankSilver( bankSilver );
  setBankGold( bankGold );
  setMobDeaths( mobDeaths );
  setMobKills( mobKills );
  setPkDeaths( pkDeaths );
  setPkKills( pkKills );
  setExp( exp );
  setTnl( tnl );
  setPractices( practices );
  setTrains( trains );
  setGains( gains );
  setOmninet( omninet );
  setTitle( title );
  setEyeDesc( eyeDesc );
  setEyeColor( eyeColor );
  setHairDesc( hairDesc );
  setHairColor( hairColor );
  setStrHeight( strHeight );
  setStrWeight( strWeight );
  setHand( hand );
  setHouse( house );
  setPoints( points );
  setEcho( echo );
  setPoofin( poofin );
  setPoofout( poofout );

  // Load unsent notes...
  while ( read.peek() != EOF ) {
    read.getline( temp, 10000 );
    if ( strcmp( temp, "**NOTE" ) == 0 )
      read >> addNote();
  }

  read.close();
  read.clear();

  return true;
}

void Character::record( const std::string& message ) { // public
  std::string file;

  file = ( getLevel() < BUILDER ) ? PLAYER_LOG_DIR : ADMIN_LOG_DIR;
  file << getName() << ".log";

  std::ofstream write( file.c_str(), std::ofstream::app );
  if ( write.fail() ) {
    default_log( message, file );
    return;
  }
  write << Timestamp::Instance().getDateTime() << "\n\t" << message << '\n';

  write.close();
  write.clear();

  return;
}

// Static methods...
std::string Character::hand2string( const unsigned& hand ) {
  switch ( hand ) {
    case LEFT:  return "left";
    case RIGHT:  return "right";
    default:
      std::string message;
      message << "Default hit in Character::hand2string() in " << __FILE__ << " line " << __LINE__;
      log( message, SERVER );
      return "{RERROR{x";
  }
}

// Associated operators...
std::ostream& operator << ( std::ostream& write, Character& character ) { // associated

  return write;
}

inline std::ostream& operator << ( std::ostream& write, Character* character ) { // associated
  return ( write << *character );
}

std::istream& operator >> ( std::istream& read, Character& character ) { // associated

  return read;
}

inline std::istream& operator >> ( std::istream& read, Character* character ) { // associated
  return ( read >> *character );
}
